using System;
using System.Collections.Generic;
using System.Collections;
using NHibernate;
using NHibernate.Criterion;
using CMS.Data;
using CMS.Core.Domain;

namespace CMS.Data
{
    public abstract class AbstractNHibernateDao<T, IdT> : IDao<T, IdT>
    {
        private Type persitentType = typeof(T);

        /// <summary>
        /// Loads an instance of type TypeOfListItem from the DB based on its ID.
        /// </summary>
        public virtual T GetById(IdT id, bool shouldLock)
        {
            T entity;

            if (shouldLock)
            {
                entity = (T)NHibernateSession.Load(persitentType, id, LockMode.Upgrade);
            }
            else
            {
                entity = (T)NHibernateSession.Load(persitentType, id);
            }

            return entity;
        }

        public virtual T GetById(IdT id)
        {
            T entity = (T)NHibernateSession.Get(persitentType, id);
            return entity;
        }

        /// <summary>
        /// Loads every instance of the requested type with no filtering.
        /// </summary>
        public List<T> GetAll()
        {
            return GetByCriteria();
        }

        /// <summary>
        /// Loads every instance of the requested type using the supplied <see cref="ICriterion" />.
        /// If no <see cref="ICriterion" /> is supplied, this behaves like <see cref="GetAll" />.
        /// </summary>
        public List<T> GetByCriteria(params ICriterion[] criterion)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);

            foreach (ICriterion criterium in criterion)
            {
                criteria.Add(criterium);
            }

            return criteria.List<T>() as List<T>;
        }

        public List<T> GetByExample(T exampleInstance, params string[] propertiesToExclude)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
            Example example = Example.Create(exampleInstance);

            foreach (string propertyToExclude in propertiesToExclude)
            {
                example.ExcludeProperty(propertyToExclude);
            }

            criteria.Add(example);

            return criteria.List<T>() as List<T>;
        }

        /// <summary>
        /// Looks for a single instance using the example provided.
        /// </summary>
        /// <exception cref="NonUniqueResultException" />
        public T GetUniqueByExample(T exampleInstance, params string[] propertiesToExclude)
        {
            List<T> foundList = GetByExample(exampleInstance, propertiesToExclude);

            if (foundList.Count > 1)
            {
                throw new NonUniqueResultException(foundList.Count);
            }

            if (foundList.Count > 0)
            {
                return foundList[0];
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// For entities that have assigned ID's, you must explicitly call Save to add a new one.
        /// See http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-declaration-id-assigned.
        /// </summary>
        public T Save(T entity)
        {
            try
            {
                NHibernateSession.Save(entity);
                this.CommitChanges();
                return entity;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return entity;
            }
        }

        /// <summary>
        /// For entities with automatatically generated IDs, such as identity, SaveOrUpdate may 
        /// be called when saving a new entity.  SaveOrUpdate can also be called to update any 
        /// entity, even if its ID is assigned.
        /// </summary>
        public T SaveOrUpdate(T entity)
        {
            try
            {
                NHibernateSession.SaveOrUpdate(entity);
                this.CommitChanges();
                return entity;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return entity;
            }
        }

        public void Delete(T entity)
        {
            try
            {
                NHibernateSession.Delete(entity);
                this.CommitChanges();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Commits changes regardless of whether there's an open transaction or not
        /// </summary>
        public void CommitChanges()
        {
            if (NHibernateSessionManager.Instance.HasOpenTransaction())
            {
                NHibernateSessionManager.Instance.CommitTransaction();
            }
            else
            {
                // If there's no transaction, just flush the changes
                NHibernateSessionManager.Instance.GetSession().Flush();
            }
        }

        /// <summary>
        /// Exposes the ISession used within the DAO.
        /// </summary>
        private ISession NHibernateSession
        {
            get
            {
                return NHibernateSessionManager.Instance.GetSession();
            }
        }
        
        #region Add new Methods extend
        public bool Update(T entity)
        {
            try
            {
                NHibernateSession.Update(entity);
                this.CommitChanges();
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }

        public List<T> GetAllOrderField(string propertyOrder, bool isAsc)
        {
            List<T> results;
            try
            {
                ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
                if (isAsc)
                    criteria.AddOrder(NHibernate.Criterion.Order.Asc(propertyOrder));
                else
                    criteria.AddOrder(NHibernate.Criterion.Order.Desc(propertyOrder));
                results = criteria.List<T>() as List<T>;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results;
        }

        public List<T> GetAllOrderMoreFields(string[] propertiesOrder, bool isAsc)
        {
            List<T> results;
            try
            {
                ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
                if (isAsc)
                    foreach (String item in propertiesOrder)
                        criteria.AddOrder(NHibernate.Criterion.Order.Asc(item));
                else
                    foreach (String item in propertiesOrder)
                        criteria.AddOrder(NHibernate.Criterion.Order.Desc(item));
                    
                results = criteria.List<T>() as List<T>;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results;
        }

        public List<T> GetFieldLikeCriteria(string profertyName, string value)
        {
            List<T> results;
            try
            {
                results = NHibernateSession.CreateCriteria(persitentType).Add(NHibernate.Criterion.Expression.Like(profertyName, value, MatchMode.Anywhere))
                    .List<T>() as List<T>;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results;
        }

        public List<T> GetFieldEqualCriteria(string profertyName, object value)
        {
            List<T> results;
            try
            {
                results = NHibernateSession.CreateCriteria(persitentType).Add(NHibernate.Criterion.Expression.Eq(profertyName, value))
                    .List<T>() as List<T>;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results;
        }

        public IList GetHQLMinMaxResult(String query, int min, int max)
        {
            IList results = null;
            try
            {
                results = NHibernateSession.CreateQuery(query)
                    .SetFirstResult(min)
                    .SetMaxResults(max)
                    .List();
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results;
        }
       
        public IList GetHQL(String query)
        {
            IList results = null;
            try
            {
                results = NHibernateSession.CreateQuery(query)
                .List();
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results;
        }

        public List<T> GetHQLListT(String query)
        {
            List<T> results = null;
            try
            {
                results = NHibernateSession.CreateQuery(query)
                .List<T>() as List<T>;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results; 

        }

        public List<T> GetListIDs(String IDs)
        {
            try
            {
                List<T> results = null;
                String strObj = persitentType.Name;
                String strSQL = @"From " + strObj + " as obj where obj.ID in (" + IDs + ")";
                results = NHibernateSession.CreateQuery(strSQL).List<T>() as List<T>;
                return results;
            }
            catch 
            {
                return new List<T>();
            }
        }

        public bool DeleteListIDs(String IDs)
        {
            try
            {
                List<T> results = null;
                String strObj = persitentType.Name;
                String strSQL = @"From " + strObj + " as obj where obj.ID in (" + IDs + ")";
                //String strFileQuery = String.Empty;
                //results = NHibernateSession.CreateQuery(strSQL).List<T>() as List<T>;
                
                NHibernateSession.Delete(strSQL);
                NHibernateSession.Flush();
                this.CommitChanges();
            }
            catch (Exception ex)
            {
                return false;
            }
            return true;
        }
        
        public bool DeleteHQL(String query)
        {
            try
            {
                NHibernateSession.Delete(query);
                NHibernateSession.Flush();
                this.CommitChanges();
            }
            catch 
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Check is exist an entity with a fieldname
        /// </summary>
        public bool IsExist(String fieldname, object value)
        {
            IList results;
            int count = 0;
            try
            {
                results = NHibernateSession.CreateCriteria(persitentType)
                .Add(NHibernate.Criterion.Expression.Eq(fieldname, value))
                .List();
                if (results != null) count = results.Count;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return count > 0;
        }

        public bool IsExist(String fieldname, object value, String MaValue)
        {
            IList results;
            int count = 0;
            try
            {
                results = NHibernateSession.CreateCriteria(persitentType)
                .Add(NHibernate.Criterion.Expression.Eq(fieldname, value))
                .Add(!NHibernate.Criterion.Expression.Eq("ID", MaValue))
                .List();
                if (results != null) count = results.Count;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return count > 0;
        }

        public bool IsExist(String fieldname, object value, object MaValue)
        {
            IList results;
            int count = 0;
            try
            {
                results = NHibernateSession.CreateCriteria(persitentType)
                .Add(NHibernate.Criterion.Expression.Eq(fieldname, value))
                .Add(!NHibernate.Criterion.Expression.Eq("ID", MaValue))
                .List();
                if (results != null) count = results.Count;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return count > 0;
        }

        public bool IsExistText(string fieldname, object value, string MaValue)
        {
            IList results;
            int count = 0;
            try
            {
                results = NHibernateSession.CreateCriteria(persitentType)
                .Add(NHibernate.Criterion.Expression.Like(fieldname, "%" + value.ToString() + "%"))
                .Add(!NHibernate.Criterion.Expression.Eq("ID", MaValue))
                .List();
                if (results != null) count = results.Count;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return count > 0;
        }

        public bool IsExistBetween(String fieldname, object value1, object value2)
        {
            IList results;
            int count = 0;
            try
            {
                results = NHibernateSession.CreateCriteria(persitentType)
                .Add(NHibernate.Criterion.Expression.Between(fieldname, value1, value2))
                .List();
                if (results != null) count = results.Count;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return count > 0;
        }

        public List<T> GetAllWithMoreFieldsAndOderBy(bool IsEqual, string[] properties, Object[] values, string[] propertiesOrder, bool isAsc)
        {
            List<T> results;
            try
            {
                ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
                
                int i = 0;
                foreach (String item in properties)
                {
                    if (IsEqual)
                        criteria.Add(NHibernate.Criterion.Expression.Eq(item, values[i]));
                    else criteria.Add(NHibernate.Criterion.Expression.Like(item, values[i].ToString(), MatchMode.Anywhere));
                    i++;
                }
                if (isAsc)
                    foreach (String item in propertiesOrder)
                        criteria.AddOrder(NHibernate.Criterion.Order.Asc(item));
                else
                    foreach (String item in propertiesOrder)
                        criteria.AddOrder(NHibernate.Criterion.Order.Desc(item));

                results = criteria.List<T>() as List<T>;
            }
            catch (Exception ex)
            {
                throw (ex);
            }
            return results;
        }

        #endregion               
    }
}
